#include <linux/err.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include "ext2.h"

static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
{
	int err = ext2_add_link(dentry, inode);
	if (!err) {
		d_instantiate(dentry, inode);
		// unlock_new_inode(inode);
		return 0;
	}
	// inode_dec_link_count(inode);
	// unlock_new_inode(inode);
	// iput(inode);
	return err;
}

static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
{
	struct inode * inode;
	ino_t ino;
	
	if (dentry->d_name.len > EXT2_NAME_LEN)
		return ERR_PTR(-ENAMETOOLONG);

	ino = ext2_inode_by_name(dir, &dentry->d_name);
	inode = NULL;
	if (ino) {
		inode = ext2_iget(dir->i_sb, ino);
		if ((IS_ERR(inode))) {
			if (PTR_ERR(inode) == -ESTALE) {
				ext2_error(dir->i_sb, __func__,
						"deleted inode referenced: %lu",
						(unsigned long) ino);
				return ERR_PTR(-EIO);
			} else {
				return ERR_CAST(inode);
			}
		}
	}
	return d_splice_alias(inode, dentry);
}

static int ext2_create(struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd)
{
	struct inode * inode = ext2_new_inode(dir, mode);
	int err = PTR_ERR(inode);
	if (!IS_ERR(inode)) {
		inode->i_op = &ext2_file_inode_operations;
		// if (ext2_use_xip(inode->i_sb)) {
		// 	inode->i_mapping->a_ops = &ext2_aops_xip;
		// 	inode->i_fop = &ext2_xip_file_operations;
		// } else if (test_opt(inode->i_sb, NOBH)) {
		// 	inode->i_mapping->a_ops = &ext2_nobh_aops;
		// 	inode->i_fop = &ext2_file_operations;
		// } else {
			inode->i_mapping->a_ops = &ext2_aops;
			inode->i_fop = &ext2_file_operations;
		// }
		// mark_inode_dirty(inode);
		err = ext2_add_nondir(dentry, inode);
	}
	return err;
}

static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
{
	struct inode * inode;
	int err = -EMLINK;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	if (dir->i_nlink >= EXT2_LINK_MAX)
		goto out;
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	// inode_inc_link_count(dir);

	inode = ext2_new_inode(dir, S_IFDIR | mode);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	err = PTR_ERR(inode);
	if (IS_ERR(inode))
		goto out_dir;
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	inode->i_op = &ext2_dir_inode_operations;
	inode->i_fop = &ext2_dir_operations;
	// if (test_opt(inode->i_sb, NOBH))
	// 	inode->i_mapping->a_ops = &ext2_nobh_aops;
	// else
		inode->i_mapping->a_ops = &ext2_aops;

	// inode_inc_link_count(inode);

	printf("this is %s(): %d\r\n", __func__, __LINE__);
	err = ext2_make_empty(inode, dir);
	if (err)
		goto out_fail;
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	err = ext2_add_link(dentry, inode);
	if (err)
		goto out_fail;
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	d_instantiate(dentry, inode);
	// unlock_new_inode(inode);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
out:
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	printf("this is %s(): %d >>> g_de->rec_len = %d\r\n", __func__, __LINE__, g_de->rec_len);
	return err;

out_fail:
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	// inode_dec_link_count(inode);
	// inode_dec_link_count(inode);
	// unlock_new_inode(inode);
	// iput(inode);
out_dir:
	// inode_dec_link_count(dir);
	goto out;
}

const struct inode_operations ext2_dir_inode_operations = {
	.create		= ext2_create,
	.lookup		= ext2_lookup,
	// .link		= ext2_link,
	// .unlink		= ext2_unlink,
	// .symlink	= ext2_symlink,
	.mkdir		= ext2_mkdir,
	// .rmdir		= ext2_rmdir,
	// .mknod		= ext2_mknod,
	// .rename		= ext2_rename,
// #ifdef CONFIG_EXT2_FS_XATTR
// 	.setxattr	= generic_setxattr,
// 	.getxattr	= generic_getxattr,
// 	.listxattr	= ext2_listxattr,
// 	.removexattr	= generic_removexattr,
// #endif
// 	.setattr	= ext2_setattr,
// 	.check_acl	= ext2_check_acl,
};
